package vn.edu.saigontech.ncms.model.info;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;

import org.hibernate.annotations.OptimisticLockType;

import vn.edu.saigontech.ncms.model.BaseEntity;
import vn.edu.saigontech.ncms.model.enumeration.AttendanceStatus;
import vn.edu.saigontech.ncms.model.result.CambridgeTest;
import vn.edu.saigontech.ncms.model.result.FinalTest;
import vn.edu.saigontech.ncms.model.result.Grade;
import vn.edu.saigontech.ncms.model.result.MidtermTest;
import vn.edu.saigontech.ncms.model.result.SecondTest;
import vn.edu.saigontech.ncms.model.studying.Attendance;
import vn.edu.saigontech.ncms.util.DateUtil;

@Entity
@org.hibernate.annotations.Entity(dynamicInsert = true, dynamicUpdate = true, optimisticLock = OptimisticLockType.ALL)
@Table(name = "ncms_offering_member")
@SuppressWarnings("serial")
public class OfferingMember extends BaseEntity {
	
	@OneToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "offering_receipt_id")
	private Receipt receipt;
	
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "offeringMember")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	private FinalTest finalTest;
	
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "offeringMember")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	private CambridgeTest cambridgeTest;
	
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "offeringMember")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	private SecondTest secondFinalTest;
	
	@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "offeringMember")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	private MidtermTest midtermTest;
	
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "offeringMember")
	@org.hibernate.annotations.Cascade(value = org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
	@OrderBy("date asc")
	private Set<Attendance> attendances = new HashSet<Attendance>();
		
	@Column(name = "observable")
	private boolean observable; 
	
	@Column(name = "active")
	private boolean active;
	
	@Column(name = "comment", length = 4000)
	private String comment;
	
	public Attendance getAttendance(Date date) {
		for(Attendance attendance : this.attendances) {
			if(attendance.getDate().equals(date)) {
				return attendance;
			}
		}
		return null;
	}
	
	public Collection<Attendance> getAllAttendances() {
		Collection<Attendance> result = new ArrayList<Attendance>();
		for(Attendance each : this.attendances) {
			if(DateUtil.isBetween(each.getDate(), this.receipt.getOffering().getStartDate(), this.receipt.getOffering().getEndDate())) {
					result.add(each);
			}
		}
		return result;
	}
	
	public Collection<Attendance> getAbsentAttendances() {
		Collection<Attendance> result = new ArrayList<Attendance>();
		for(Attendance each : this.getAllAttendances()) {
			if(AttendanceStatus.ABSENT.equals(each.getStatus())) {
				result.add(each);
			}
		}
		return result;
	}
	
	public Double getAttendanceScore() {
		int n = this.getAbsentAttendances().size();
		if(n <= 2)
			return 100D;
		if(n <= 4)
			return 90D;
		if(n <= 6)
			return 80D;
		if(n <= 8)
			return 70D;
		if(n <= 90)
			return 60D;
		return 0D;
	}
	
	public Double getConvertedAttendanceScore() {
		return this.getAttendanceScore() / 10;
	}

	public Double getTotalScore() {
		try {
			double total = this.getConvertedAttendanceScore() + this.midtermTest.getConvertedScore() + this.finalTest.getConvertedWrittenScore() + this.finalTest.getConvertedOralScore();
			if((total % 0.5) == 0) return total;
			return (double)Math.round(total);
		}
		catch(Exception ex) {}
		return null;
	}
	
	public Double getTotalScore1() {
		return (this.getTotalScore() == null)? this.finalTest.getOralScore() : this.getTotalScore();
	}
	
	@Deprecated
	public Grade getGrade1() {
		return (this.getGrade() == null)? this.finalTest.getOldGrade() : this.getGrade();
	}
	
	public String getGradeHistory() {
		Grade grade = getGrade();
		if(grade != null) {
			return this.getTotalScore() + " (" + grade + ")";
		}
		return "";
	}
	
	public Grade getGrade() {
		Double total = this.getTotalScore();
		if(total == null) return null;
		if(total >= 90 && total <= 100) {
			return Grade.A;
		}
		else if(total >= 80 && total < 90) {
			return Grade.B;
		}
		else if(total >= 70 && total < 80) {
			return Grade.C;
		}
		else if(total >= 60 && total < 70) {
			return Grade.D;
		}
		else if(total >= 50 && total < 60) {
			return Grade.E;
		}
		else if(total >= 0 && total < 50) {
			return Grade.F;
		}
		return null;
	}
	
	public boolean passed() {
		Grade grade = this.getGrade();
		return grade != null && !Grade.F.equals(grade);
	}
	
	public boolean isConsecutive() {
		Offering offering = this.receipt.getOffering();
		Date lowDate = DateUtil.addDay(offering.getStartDate(), -21); 
		for(AbstractReceipt receipt : this.receipt.getStudent().getOfferingReceipts()) {
			if(this.equals(receipt.getOffering()))
				continue;
			Date endDate = receipt.getOffering().getEndDate();
			Date startDate = offering.getStartDate();
			if(DateUtil.isBetween(endDate, lowDate, startDate)) {
				return true;
			}
			
		}
		return false;
	}
	
	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	public Receipt getReceipt() {
		return receipt;
	}

	public void setReceipt(Receipt receipt) {
		this.receipt = receipt;
	}

	public boolean isObservable() {
		return observable;
	}

	public void setObservable(boolean observable) {
		this.observable = observable;
	}

	public FinalTest getFinalTest() {
		return finalTest;
	}

	public void setFinalTest(FinalTest finalTest) {
		this.finalTest = finalTest;
	}

	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}

	public Set<Attendance> getAttendances() {
		return attendances;
	}

	public void setAttendances(Set<Attendance> attendances) {
		this.attendances = attendances;
	}

	public MidtermTest getMidtermTest() {
		return midtermTest;
	}

	public void setMidtermTest(MidtermTest midtermTest) {
		this.midtermTest = midtermTest;
	}

	public SecondTest getSecondFinalTest() {
		return secondFinalTest;
	}

	public void setSecondFinalTest(SecondTest secondFinalTest) {
		this.secondFinalTest = secondFinalTest;
	}

	public CambridgeTest getCambridgeTest() {
		return cambridgeTest;
	}

	public void setCambridgeTest(CambridgeTest cambridgeTest) {
		this.cambridgeTest = cambridgeTest;
	}
}